package org.elsfs.framework.event.support;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 事件侦听器，用于调用接受单个事件参数的目标对象的方法。
 *
 * @author zeng
 * @since 0.0.1
 */
public class SingleArgumentMethodEventListener implements TypedEventListener {

  private final Object target;

  private final Method method;

  /**
   * 注解事件监听器
   *
   * @param target 目标
   * @param method 方法
   */
  public SingleArgumentMethodEventListener(Object target, Method method) {
    this.target = target;
    this.method = method;
    // assert that the method is defined as expected:
    getMethodArgumentType(method);

    assertPublicMethod(method);
  }

  /**
   * 获取target
   *
   * @return target
   */
  public Object getTarget() {
    return this.target;
  }

  /**
   * 获取method
   *
   * @return method
   */
  public Method getMethod() {
    return this.method;
  }

  /**
   * 断言方法
   *
   * @param method void
   */
  private void assertPublicMethod(Method method) {
    int modifiers = method.getModifiers();
    if (!Modifier.isPublic(modifiers)) {
      throw new IllegalArgumentException("Event handler method [" + method + "] must be public.");
    }
  }

  @Override
  public boolean accepts(Object event) {
    return event != null && getEventType().isInstance(event);
  }

  @Override
  public Class getEventType() {
    return getMethodArgumentType(getMethod());
  }

  @Override
  public void onEvent(Object event) {
    Method method = getMethod();
    try {
      method.invoke(getTarget(), event);
    } catch (Exception e) {
      throw new IllegalStateException("Unable to invoke event handler method [" + method + "].", e);
    }
  }

  /**
   * 获取方法参数类型
   *
   * @param method 方法
   * @return 方法参数类型
   */
  protected Class getMethodArgumentType(Method method) {
    Class[] paramTypes = method.getParameterTypes();
    if (paramTypes.length != 1) {
      // the default implementation expects a single typed argument and nothing
      // more:
      String msg = "Event handler methods must accept a single argument.";
      throw new IllegalArgumentException(msg);
    }
    return paramTypes[0];
  }
}
